pub extern crate cesu8;
extern crate jnim_sys as sys;

pub use sys::{
	JNI_VERSION_1_1, //
	JNI_VERSION_1_2,
	JNI_VERSION_1_4,
	JNI_VERSION_1_6,
};

mod env;
pub use env::*;

mod vm;
pub use vm::*;

mod object;
pub use object::*;

mod ids;
pub use ids::*;

mod rc;
pub use rc::*;

mod weak;
pub use weak::*;

mod prim;
pub use prim::*;

// mod sign;
// pub use sign::*;

mod value;
pub use value::*;

mod local;
pub use local::*;

mod string;
pub use string::*;

mod throw;
pub use throw::*;

mod array;
pub use array::*;

mod maker;
pub use maker::*;

#[macro_export]
macro_rules! JSuperCall {
	($this:ident,$env:ident,$fun_name:literal,$sign:expr,$($arg:expr,)*) => {
		{
			let this:&$crate::JObject = $this.as_ref();
			if let Some(this_class) = this.as_ref().class($env) {
				if let Some(super_class) = this_class.supper($env) {
					static CACHE: $crate::CachedID = $crate::CachedID::new();
					if let Some(method) = CACHE.get(|| { super_class.method($env, $fun_name, $sign) }) {
						method.call_non_virtual($env, this, super_class, ($($arg,)*))
					} else {
						None
					}
				} else {
					None
				}
			} else {
				None
			}
		}
	};
}
#[macro_export]
macro_rules! JnimDefineClass {
	(
		$(#[$m:meta])*
		#CLASS[$JAVA_NAME:literal]
		$v:vis struct $TYPE:ident {
			$($field:tt)*
		}
	) => {
		$(#[$m])*
		$v struct $TYPE {
			__object: $crate::JObject,
			$($field)*
		}
		impl $crate::JNameMaker for $TYPE {
			const JAVA_PATH: &'static [u8] = $JAVA_NAME;
		}
		impl AsRef<$crate::JObject> for $TYPE {
			fn as_ref(&self) -> &$crate::JObject {
				&self.__object
			}
		}
		impl $crate::JPlainMarker for $TYPE {
			fn class(env: Option<&$crate::JEnv>) -> Option<$crate::JClassID> {
				debug_assert_eq!(0, core::mem::size_of::<$TYPE>());
				use $crate::JNameMaker as _;
				static CACHE: $crate::CachedID = $crate::CachedID::new();
				CACHE.get(|| $crate::JEnv::env(env)?.find_class(Self::JAVA_PATH))
			}
		}
	};
}
#[macro_export]
macro_rules! JnimNativeMethods {
	(impl $TYPE:ident {
		$(
			#USE[$JAVA_NAME1:literal,$JAVA_SIGN1:literal]
			fn $FUN_NAME1:ident;
		)*
		$(
			#DEF[$JAVA_NAME2:literal,$JAVA_SIGN2:literal]
			fn $FUN_NAME2:ident ($($PARAMS2:tt)*) $(-> $RET2:ty)? $BODY2:block
		)*
		$(
			#SUPER[$JAVA_NAME3:literal,$JAVA_SIGN3:literal]
			fn $FUN_NAME3:ident ($($PARAMS3:tt)*) $(-> $RET3:ty)? : ($($SUPER_PARAMS3:tt)*) { $($IMPL3:tt)* }
		)*
	}) => {
		const _: () = {
			impl $TYPE {
				pub fn register_natives(vm: $crate::JVm) -> Result<(), $crate::JInt> {
					let env = vm.env().ok_or(1)?;
					use $crate::JPlainMarker as _;
					let class = $TYPE::class(Some(env)).ok_or(2)?;
					env.register_natives(class, &METHODS)
				}
			}
			const METHODS: &'static [$crate::JNativeMethod] = &[
				$($crate::JEnv::native($JAVA_NAME1,$JAVA_SIGN1,$TYPE::$FUN_NAME1 as _),)*
				$($crate::JEnv::native($JAVA_NAME2,$JAVA_SIGN2,$FUN_NAME2 as _),)*
				$($crate::JEnv::native($JAVA_NAME3,$JAVA_SIGN3,$FUN_NAME3 as _),)*
			];
			$(extern fn $FUN_NAME2($($PARAMS2)*) $(-> $RET2)? $BODY2)*
			$(extern fn $FUN_NAME3($($PARAMS3)*) $(-> $RET3)? {
				$($IMPL3)*
				let _: () = $crate::JSuperCall!($($SUPER_PARAMS3)*).unwrap();
			})*
		};
	};
}
